package xj.graph2d.app.prop;

import static xj.app.BaseApplicationFrame.getResourceString;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;

import javax.swing.JFrame;

import xj.graph2d.app.DrawApp;
import xj.graph2d.shapes.uml.ClassShape;
import xj.graph2d.table.FeatureTableEntry;
import xj.model.uml.Attribute;
import xj.model.uml.ClassDiagram;
import xj.model.uml.Operation;
import xj.model.uml.UMLClass;
import xj.model.uml.UMLDiagram;
import xj.model.uml.Visibility;
import xj.model.uml.SuperType;
import xj.model.uml.SuperType.Kind;

public class UMLClassModelPane extends UMLBasicModelPane {

  public UMLClassModelPane(DrawApp main) {
    super(main);

    className = new ModelPropertiesPane.NameField("name", 
						  getResourceString("ClassName.text"));
    addRow(className);

    templates = new ModelPropertiesPane.NameField("templates", 
						  getResourceString("Templates.text"));

    addRow(templates);

    packageName = new ModelPropertiesPane.NameField("packageName", 
						    getResourceString("PackageName.text"));

    addRow(packageName);

    Visibility[] values = { Visibility.Public, Visibility.Package };
    visibility = new ModelPropertiesPane.ChoiceField("visibility", 
						     getResourceString("Visibility.text"), 
						     values);
    addRow(visibility);

    isInterfaceCB = new ModelPropertiesPane.CheckBoxField("interface", 
							  getResourceString("IsInterface.text"));
    addRow(isInterfaceCB);
    isAbstractCB = new ModelPropertiesPane.CheckBoxField("abstract", 
							 getResourceString("IsAbstract.text"));
    addRow(isAbstractCB);
    isActiveCB = new ModelPropertiesPane.CheckBoxField("active", 
						       getResourceString("IsActive.text"));
    addRow(isActiveCB);

    isMixinCB = new ModelPropertiesPane.CheckBoxField("mixin", 
						      getResourceString("IsMixin.text"));
    addRow(isMixinCB);

    isExceptionCB = new ModelPropertiesPane.CheckBoxField("exception", 
							  getResourceString("IsException.text"));
    addRow(isExceptionCB);

    isInterfaceCB.addExclude(isAbstractCB);
    isInterfaceCB.addExclude(isMixinCB);
    isInterfaceCB.addExclude(isExceptionCB);
    isInterfaceCB.addExclude(isMixinCB);

    isAbstractCB.addExclude(isExceptionCB);
    isActiveCB.addExclude(isExceptionCB);
    isMixinCB.addExclude(isExceptionCB);

    isAbstractCB.addExclude(isMixinCB);
    isActiveCB.addExclude(isMixinCB);
    isExceptionCB.addExclude(isMixinCB);

    superTypes = new SuperTypeField(main.getJFrame(), this);
    addRow(superTypes);

    attributes = new AttributesField(main.getJFrame());
    addRow(attributes);
    operations = new OperationsField(main.getJFrame());
    addRow(operations);

    addBasicFields();

    addFiller();
  }

  protected ModelPropertiesPane.NameField className;

  // protected JComboBox visibility;

  protected ModelPropertiesPane.ChoiceField visibility;

  protected ModelPropertiesPane.CheckBoxField isInterfaceCB;

  protected ModelPropertiesPane.CheckBoxField isAbstractCB;

  protected ModelPropertiesPane.CheckBoxField isActiveCB;

  protected ModelPropertiesPane.CheckBoxField isMixinCB;

  protected ModelPropertiesPane.CheckBoxField isExceptionCB;

  protected ModelPropertiesPane.NameField templates;

  protected ModelPropertiesPane.NameField packageName;

  protected SuperTypeField superTypes;

  protected AttributesField attributes;

  protected OperationsField operations;

  protected static final boolean debug = false;

  protected static String getClassName(UMLClass cls) {
    String cname = null;
    if (cls != null) {
      cname = cls.getName();
      if (cname != null) {
	cname = cname.trim();
	if (cname.length() == 0) {
	  cname = "<unnamed>";
	}
      } else {
	cname = "<unnamed>";
      }
    } else {
      cname = "<unnamed>";
    }
    return cname;
  }

  protected static String getClassType(UMLClass cls) {
    String type = "class";
    if (cls != null) {
      if (cls.isInterface()) {
	type = "interface";
      } else if (cls.isAbstract()) {
	type = "abstract class";
      }
    }
    return type;
  }

  static class AttributesField extends FeatureListField {

    AttributesField(JFrame owner) {
      super(owner, 
	    getResourceString("Attributes.text"),
	    getResourceString("Attributes.dialog.title"),
	    "attribute");
      setDialogSize(900, 600);
    }

    @Override
    protected void updateTips() {
      if (editor != null) {
	String prefix = "<html><body bgcolor='white'><b>Attributes</b><br>";
	String suffix = "</body></html>";
	StringBuilder sb = new StringBuilder();
	if (model instanceof UMLClass) {
	  UMLClass cls = (UMLClass) model;
	  java.util.List<Attribute> attrs = cls.getAttributes();
	  if (attrs != null) {
	    Iterator<Attribute> iter = attrs.iterator();
	    while (iter.hasNext()) {
	      Attribute attr = iter.next();
	      sb.append("&nbsp;&nbsp;");
	      if (attr.isStatic()) {
		sb.append("<u>" + attr.toString(true) + "</u>");
	      } else {
		sb.append(attr.toString(true));
	      }
	      String prop = attr.getPropertiesString();
	      if (prop != null) {
		sb.append(" ").append(prop);
	      }
	      sb.append("<br>");
	    }
	  }
	}

	String text = null;
	if (sb.length() > 0) { 
	  text = sb.toString();
	} else { 
	  text = "&nbsp;&nbsp;<i>none</i>";
	}
	editor.setToolTipText(prefix + text + suffix);
      }
    }

    @Override
    public void updateEditorValue() {
      if (debug) {
	System.out.println("AttributeField.updateEditorValue()");
      }

      if (isActive()) { 
	java.util.List<FeatureTableEntry> entries = featurePanel.getEntries();
	if (entries != null) {
	  entries.clear();
	} else {
	  entries = new ArrayList<FeatureTableEntry>();
	}
	if (model instanceof UMLClass) {
	  UMLClass cls = (UMLClass) model;
	  
	  featurePanel.setTableTitle("Attributes of", getClassName(cls), " [" + getClassType(cls) + "]");
	  
	  java.util.List<Attribute> attrs = cls.getAttributes();
	  
	  if (debug) {
	    System.out.println("AttributeField.updateEditorValue() #attr="
			       + attrs.size() + " #ownedElements="
			       + cls.getOwnedElements().size());
	  }
	  
	  if (attrs != null) {
	    Iterator<Attribute> iter = attrs.iterator();
	    while (iter.hasNext()) {
	      Attribute attr = iter.next();
	      entries.add(new UMLAttributeEntry(attr));
	    }
	  }
	}
	if (entries.isEmpty()) {
	  entries.add(makeNewEntry());
	}
	featurePanel.setEntries(entries);
      }
    }

    @Override
    public void updateModelValue() {
      if (debug) {
	System.out.println("AttributeField.updateModelValue()");
      }

      java.util.List<FeatureTableEntry> entries = featurePanel.getEntries();
      if (model instanceof UMLClass) {
	UMLClass cls = (UMLClass) model;
	cls.removeAllAttributes();
	if (entries != null) {
	  Iterator<FeatureTableEntry> iter = entries.iterator();
	  while (iter.hasNext()) {
	    FeatureTableEntry fe = iter.next();
	    if (fe instanceof UMLAttributeEntry) {
	      UMLAttributeEntry entry = (UMLAttributeEntry) fe;
	      Attribute attr = entry.getAttribute();
	      String name = attr.getName();
	      if (name != null) {
		name = name.trim();
		if (name.length() > 0) {
		  cls.addAttribute(attr);
		}
	      }
	    }
	  }
	}
	if (shape instanceof ClassShape) {
	  ClassShape cs = (ClassShape) shape;
	  cs.fitCompartments();
	}

	updateTips();
      }
    }

    @Override
    protected String getTextLabel() {
      return "attribute";
      /*
      String label = "attribute ";
      if (name != null && name.length() > 0) {
	label += name;
      } else {
	label += "<unnamed>";
      }
      if (model instanceof UMLClass) {
	UMLClass cls = (UMLClass) model;
	label += (" of " + getClassName(cls));
      }
      return label;
      */
    }

    static FeatureTableEntry proto = new UMLAttributeEntry(null);

    @Override
    FeatureTableEntry getPrototype() {
      return proto;
    }

    @Override
    FeatureTableEntry makeNewEntry() {
      Attribute attr = new Attribute();
      attr.setVisibility(Visibility.Protected);
      return new UMLAttributeEntry(attr);
    }

  }

  static class OperationsField extends FeatureListField {

    OperationsField(JFrame owner) {
      super(owner, 
	    getResourceString("Operations.text"),
	    getResourceString("Operations.dialog.title"), 
	    true, true, "operation");
      setDialogSize(900, 600);
    }

    @Override
    protected void updateTips() {
      if (editor != null) {
	String prefix = "<html><body bgcolor='white'><b>Operations</b><br>";
	String suffix = "</body></html>";
	StringBuilder sb = new StringBuilder();
	if (model instanceof UMLClass) {
	  UMLClass cls = (UMLClass) model;
	  java.util.List<Operation> opers = cls.getOperations();
	  if (opers != null) {
	    Iterator<Operation> iter = opers.iterator();
	    while (iter.hasNext()) {
	      Operation oper = iter.next();
	      sb.append("&nbsp;&nbsp;");
	      if (oper.isStatic()) {
		sb.append("<u>" + oper.toString(true) + "</u>");
	      } else if (oper.isAbstract()) {
		sb.append("<i>" + oper.toString(true) + "</i>");
	      } else {
		sb.append(oper.toString(true));
	      }
	      String prop = oper.getPropertiesString();
	      if (prop != null) {
		sb.append(" ").append(prop);
	      }
	      sb.append("<br>");
	    }
	  }
	}

	String text = null;
	if (sb.length() > 0) { 
	  text = sb.toString();
	} else { 
	  text = "&nbsp;&nbsp;<i>none</i>";
	}
	editor.setToolTipText(prefix + text + suffix);
      }
    }

    @Override
    public void updateEditorValue() {
      if (isActive()) { 
	java.util.List<FeatureTableEntry> entries = featurePanel.getEntries();
	if (entries != null) {
	  entries.clear();
	} else {
	  entries = new ArrayList<FeatureTableEntry>();
	}
	if (model instanceof UMLClass) {
	  UMLClass cls = (UMLClass) model;
	  featurePanel.setTableTitle("Operations of", getClassName(cls), " [" + getClassType(cls) + "]");

	  java.util.List<Operation> operations = cls.getOperations();
	  if (operations != null) {
	    Iterator<Operation> iter = operations.iterator();
	    while (iter.hasNext()) {
	      Operation m = iter.next();
	      entries.add(new UMLOperationEntry(m));
	    }
	  }
	}
	if (entries.isEmpty()) {
	  entries.add(makeNewEntry());
	}
	featurePanel.setEntries(entries);
      }
    }

    @Override
    public void updateModelValue() {
      java.util.List<FeatureTableEntry> entries = featurePanel.getEntries();
      if (entries != null && model instanceof UMLClass) {
	UMLClass cls = (UMLClass) model;
	cls.removeAllOperations();
	Iterator<FeatureTableEntry> iter = entries.iterator();
	while (iter.hasNext()) {
	  FeatureTableEntry fe = iter.next();
	  if (fe instanceof UMLOperationEntry) {
	    UMLOperationEntry entry = (UMLOperationEntry) fe;
	    Operation m = entry.getOperation();
	    String name = m.getName();
	    if (name != null) {
	      name = name.trim();
	      if (name.length() > 0) {
		cls.addOperation(m);
	      }
	    }
	  }
	}

	if (shape instanceof ClassShape) {
	  ClassShape cs = (ClassShape) shape;
	  cs.fitCompartments();
	}

	updateTips();
      }
    }

    @Override
    protected String getTextLabel() {
      return "operation";
      /*
      String label = "operation ";
      if (name != null && name.length() > 0) {
	label += name;
      } else {
	label += "<unnamed>";
      }
      if (model instanceof UMLClass) {
	UMLClass cls = (UMLClass) model;
	label += (" of " + getClassName(cls));
      }
      return label;
      */
    }

    static FeatureTableEntry proto = new UMLOperationEntry(null);

    @Override
    FeatureTableEntry getPrototype() {
      return proto;
    }

    @Override
    FeatureTableEntry makeNewEntry() {
      Operation m = new Operation();
      m.setVisibility(Visibility.Public);
      return new UMLOperationEntry(m);
    }

  }

  static class SuperTypeField extends FeatureListField {

    SuperTypeField(JFrame owner, PropertiesPane propPane) {
      super(owner, 
	    getResourceString("SuperTypes.text"),
	    getResourceString("SuperTypes.dialog.title"), 
	    false, false);
      this.propPane = propPane;
      setDialogSize(470, 300);
    }

    @Override
    protected void updateTips() {
      if (editor != null) {
	String prefix = "<html><body bgcolor='white'><b>Super types</b><br>";
	String suffix = "</body></html>";
	StringBuilder sb = new StringBuilder();
	if (model instanceof UMLClass) {
	  UMLClass cls = (UMLClass) model;

	  // super classes defined by generalizations in the class diagram
	  Set<UMLClass> genClasses = null; 
	  if (propPane != null) {
	    UMLDiagram d = propPane.getCurrentUMLDiagram();
	    if (d instanceof ClassDiagram) {
	      ClassDiagram cd = (ClassDiagram) d;
	      genClasses = cd.findSuperTypes(cls);
	      if (genClasses != null && !genClasses.isEmpty()) {
		Iterator<UMLClass> iter = genClasses.iterator();
		while (iter.hasNext()) {
		  UMLClass c = iter.next(); 
		  sb.append("&nbsp;&nbsp;");
		  sb.append(c.getName() + " (<i>"); 
		  sb.append((c.isInterface() ? "Realization" : "Generalization"));
		  sb.append("</i>)<br>");
		}
	      }
	    }
	  }

	  java.util.List<SuperType> slist = cls.getSuperTypes();
	  if (slist != null && !slist.isEmpty()) {
	    Iterator<SuperType> iter = slist.iterator();
	    while (iter.hasNext()) {
	      SuperType s = iter.next();
	      sb.append("&nbsp;&nbsp;");
	      sb.append(s.getName() + " (<i>"); 
	      sb.append(s.getKind().toString());
	      sb.append("</i>)<br>");
	    }
	  }
	}

	String text = null;
	if (sb.length() > 0) { 
	  text = sb.toString();
	} else { 
	  text = "&nbsp;&nbsp;<i>none</i>";
	}
	editor.setToolTipText(prefix + text + suffix);
      }
    }

    @Override
    public void updateEditorValue() {
      if (isActive()) { 
	java.util.List<FeatureTableEntry> entries = featurePanel.getEntries();
	if (entries != null) {
	  entries.clear();
	} else {
	  entries = new ArrayList<FeatureTableEntry>();
	}
	if (model instanceof UMLClass) {
	  UMLClass cls = (UMLClass) model;
	  // super classes defined by generalizations in the class diagram
	  Set<UMLClass> genClasses = null; 
	  if (propPane != null) {
	    UMLDiagram d = propPane.getCurrentUMLDiagram();
	    if (d instanceof ClassDiagram) {
	      ClassDiagram cd = (ClassDiagram) d;
	      genClasses = cd.findSuperTypes(cls);
	    }
	  }
	  
	  featurePanel.setTableTitle("Super types of", getClassName(cls),
				     " [" + getClassType(cls) + "]");

	  if (genClasses != null) {
	    Iterator<UMLClass> giter = genClasses.iterator();
	    while (giter.hasNext()) {
	      UMLClass c = giter.next();
	      UMLSuperTypeEntry entry = 
		new UMLSuperTypeEntry(c.getName() + " (defined in the diagram)",
				      (c.isInterface() ? Kind.Realization : Kind.Generalization));
	      entry.setEditable(false);
	      entries.add(entry);
	    }
	  }
	  
	  // System.out.println("SuperTypeField.updateEditorValue()
	  // genClasses=" + genClasses);

	  java.util.List<SuperType> slist = cls.getSuperTypes();
	  if (slist != null) {
	    Iterator<SuperType> iter = slist.iterator();
	    while (iter.hasNext()) {
	      SuperType s = iter.next();
	      if (genClasses == null || !genClasses.contains(s.getName())) {
		UMLSuperTypeEntry entry = new UMLSuperTypeEntry(s);
		entry.setEditable(true);
		entries.add(entry);
	      }
	    }
	  }
	}
	if (entries.isEmpty()) {
	  entries.add(makeNewEntry());
	}
	featurePanel.setEntries(entries);
      }
    }

    @Override
    public void updateModelValue() {
      java.util.List<FeatureTableEntry> entries = featurePanel.getEntries();
      if (entries != null && model instanceof UMLClass) {
	UMLClass cls = (UMLClass) model;
	cls.removeAllSuperTypes();
	Iterator<FeatureTableEntry> iter = entries.iterator();
	while (iter.hasNext()) {
	  FeatureTableEntry fe = iter.next();
	  if (fe instanceof UMLSuperTypeEntry) {
	    UMLSuperTypeEntry entry = (UMLSuperTypeEntry) fe;
	    if (entry.isEditable()) {
	      String name = entry.getName();
	      if (name != null) {
		name = name.trim();
		if (name.length() > 0) {
		  System.out.println("SuperTypeField.updateModelValue() " + name + " " + entry.getKind());

		  cls.addSuperType(new SuperType(name, entry.getKind()));
		}
	      }
	    }
	  }
	}
	updateTips();
      }
    }

    protected PropertiesPane propPane;

    static FeatureTableEntry proto = new UMLSuperTypeEntry(null);

    @Override
    FeatureTableEntry getPrototype() {
      return proto;
    }

    @Override
    FeatureTableEntry makeNewEntry() {
      return new UMLSuperTypeEntry(null);
    }

  }

}
